package com.boot.security.service.cluster;

import com.boot.security.model.po.Resource;
import com.boot.security.model.vo.SourceKey;
import com.boot.security.service.authorization.AccessSource;
import com.boot.security.service.authorization.AccessSourceClusterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.function.Consumer;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @description 消息队列处理更新，保证最终一致性
 * 需要考虑在同一条记录上的操作
 * 1. saveOne updateOne -> updateOne saveOne
 * updateOne试图先删除saveOne插入后的资源映射，实际上AccessSource中没有该映射，接着直接加入最新的映射；saveOne覆盖映射
 * 2. updateOne removeOne -> removeOne updateOne
 * removeOne试图删除updateOne更新后的资源的映射，实际上AccessSource中没有该映射，未修改；
 * updateOne先删除oldResource映射，删除成功后试图加入最新的映射，实际上数据库没有此资源
 * 3. saveOne removeOne -> removeOne saveOne
 * removeOne试图删除updateOne更新后的资源的映射，实际上AccessSource中没有该映射，未修改；
 * saveOne试图加入最新的映射，实际上数据库没有此资源，未修改。
 * @date 2021/8/1 22:57
 **/
@Slf4j
@Service("security_access_source_cluster_service")
public class StreamAccessSourceClusterHandlerImpl implements AccessSourceClusterService, Consumer<Message<AccessSourceClusterChange>> {

    private final AccessSource accessSource;

    public StreamAccessSourceClusterHandlerImpl(AccessSource accessSource) {
        this.accessSource = accessSource;
    }

    @Override
    public void saveOne(SourceKey sourceKey, Long resourceId) {
        accessSource.saveOne(resourceId, sourceKey);
    }

    @Override
    public void saveMulti(SourceKey sourceKey, List<Long> resourceIds) {
        accessSource.saveMulti(resourceIds, sourceKey);
    }

    @Override
    public void updateOne(SourceKey sourceKey, Resource oldResource) {
        accessSource.updateOne(oldResource, sourceKey);
    }

    @Override
    public void removeOne(SourceKey sourceKey, Resource oldResource) {
        accessSource.removeOne(oldResource, sourceKey);
    }

    @Override
    public void removeMulti(SourceKey sourceKey, List<Resource> resources) {
        accessSource.removeMulti(resources, sourceKey);
    }

    @Override
    public void accept(Message<AccessSourceClusterChange> accessSourceClusterChangeMessage) {
        AccessSourceClusterChange payload = accessSourceClusterChangeMessage.getPayload();
        log.info("accept AccessSourceClusterChange: {}", payload);

        SourceKey currentSourceKey = accessSource.getSourceKey();
        SourceKey payloadSourceKey = payload.getSourceKey();
        if (currentSourceKey.equals(payloadSourceKey)) {
            log.info("收到了本机发送的规则修正请求，已忽略该请求!");
        } else {
            switch (payload.getType()) {
                case AccessSourceClusterChange.SAVE_ONE:
                    saveOne(payload.getSourceKey(), payload.getResourceId());
                    break;
                case AccessSourceClusterChange.SAVE_MULTI:
                    saveMulti(payload.getSourceKey(), payload.getResourceIds());
                    break;
                case AccessSourceClusterChange.UPDATE_ONE:
                    updateOne(payload.getSourceKey(), payload.getResource());
                    break;
                case AccessSourceClusterChange.REMOVE_ONE:
                    removeOne(payload.getSourceKey(), payload.getResource());
                    break;
                case AccessSourceClusterChange.REMOVE_MULTI:
                    removeMulti(payload.getSourceKey(), payload.getResources());
                    break;
                default:
                    log.error("Unknown type {} in {}", payload.getType(), AccessSourceClusterChange.class.getSimpleName());
            }
        }
        //        if (currentSourceKey.equals(payloadSourceKey)) {
        //            log.info("收到了本机发送的规则修正请求，已忽略该请求!");
        //        } else if (currentSourceKey.getTime().isAfter(payloadSourceKey.getTime())) {
        //            log.warn("收到了过期的规则修正请求，已忽略该请求!");
        //        } else {
        //            switch (payload.getType()) {
        //                case AccessSourceClusterChange.SAVE_ONE:
        //                    saveOne(payload.getSourceKey(), payload.getResourceId());
        //                    break;
        //                case AccessSourceClusterChange.SAVE_MULTI:
        //                    saveMulti(payload.getSourceKey(), payload.getResourceIds());
        //                    break;
        //                case AccessSourceClusterChange.UPDATE_ONE:
        //                    updateOne(payload.getSourceKey(), payload.getResource());
        //                    break;
        //                case AccessSourceClusterChange.REMOVE_ONE:
        //                    removeOne(payload.getSourceKey(), payload.getResource());
        //                    break;
        //                case AccessSourceClusterChange.REMOVE_MULTI:
        //                    removeMulti(payload.getSourceKey(), payload.getResources());
        //                    break;
        //                default:
        //                    log.error("Unknown type {} in {}", payload.getType(), AccessSourceClusterChange.class.getSimpleName());
        //            }
        //        }
    }
}
